/*
 * Copyright (C) 2010 Fred Barrie This program is free software: you can
 * redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version. This program is distributed
 * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details. You should have received
 * a copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package org.aergia.vinny.gui.view;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Calendar;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JTextField;

import org.aergia.vinny.core.ApiResult;
import org.aergia.vinny.core.enums.TimeFrame;
import org.aergia.vinny.database.pojo.Service;
import org.aergia.vinny.database.pojo.ServiceType;
import org.aergia.vinny.database.pojo.Skill;
import org.aergia.vinny.database.pojo.VinnyOption;
import org.aergia.vinny.database.pojo.VinnyRole;
import org.aergia.vinny.database.pojo.VinnyUser;
import org.aergia.vinny.database.pojo.Volunteer;
import org.aergia.vinny.gui.controller.VinnyController;
import org.aergia.vinny.gui.util.DialogUtil;
import org.aergia.vinny.gui.util.FontUtil;
import org.aergia.vinny.gui.util.LabelPanel;
import org.aergia.vinny.gui.util.TimeEntry;
import org.aergia.vinny.gui.view.EditorDialog.EditorPanel;
import org.aergia.vinny.util.DateUtil;
import org.freixas.jcalendar.DateEvent;
import org.freixas.jcalendar.DateListener;
import org.freixas.jcalendar.JCalendarCombo;

public class ServiceEditor extends EditorPanel implements ActionListener {

	private static final long serialVersionUID = 1L;

	protected JComboBox approver;
	protected JTextField commentField;
	protected final VinnyController controller;
	protected JTextField durationField;
	protected TimeEntry endTimeField;
	protected JCheckBox serviceActive;
	protected JCheckBox serviceApproved;
	protected JComboBox serviceTypes;
	protected JCalendarCombo startDate;
	protected TimeEntry startTimeField;
	protected JComboBox timeFrameCombo;
	protected JComboBox volunteer;

	private final int firstMonthOfYear;
	private final Pattern pattern = Pattern.compile("(\\d{1,2}):(\\d{2})");
	private Service service;
	private final VinnyUser user;

	public ServiceEditor(final VinnyController controller, final VinnyUser user) {
		this(controller, user, null);
	}

	/**
	 * @param controller
	 * @param volunteer
	 */
	public ServiceEditor(final VinnyController controller, final VinnyUser user, final Service service) {
		this.controller = controller;
		this.user = user;
		this.service = service;
		this.initComponents();
		this.firstMonthOfYear = DateUtil.getMonth(controller.getOption(VinnyOption.FIRST_MONTH_OF_YEAR, "0"));
	}

	@Override
	public void actionPerformed(final ActionEvent e) {

		if (e.getSource() == this.serviceApproved) {

			if (this.approver.getSelectedItem() == null) {
				this.approver.setSelectedItem(this.user);
			}

			if (this.serviceApproved.isSelected() && !this.service.isApproved() && this.commentField.getText().isEmpty()) {
				this.commentField.setText("Approved on " + DateUtil.format(DateUtil.getInstance()) + ".");
			}

		}

		if (e.getSource() == this.endTimeField) {
			final Calendar startTime = this.calcStartTime(this.firstMonthOfYear);
			final Calendar endTime = this.calcEndTime(startTime, this.firstMonthOfYear);

			final long duration = DateUtil.getDifference(startTime, endTime);
			if (duration < 0) {
				this.durationField.setText("0");
			} else {
				this.durationField.setText(DateUtil.formatDuration(duration));
			}
		} else {

			long duration = 0;
			final Calendar startTime = this.calcStartTime(this.firstMonthOfYear);
			Calendar endTime = DateUtil.getInstance();

			try {
				duration = Long.parseLong(this.durationField.getText());
				endTime = this.calcEndTime(this.calcStartTime(this.firstMonthOfYear), duration, this.firstMonthOfYear);
			} catch (final Exception exception) {
				this.durationField.setText("0");
				DialogUtil.alert(this, "Duration must be greater than zero.", "Invalid data");
				return;
			}

			if (e.getSource() == this.durationField) {
				endTime = this.calcEndTime(startTime, duration, this.firstMonthOfYear);
				this.endTimeField.setTime(endTime);
			}
		}
	}

	public Service getService() {
		return this.service;
	}

	@Override
	public String getTitle() {
		return "Service Editor";
	}

	@Override
	public boolean saveAction() {
		if (this.service == null) {
			this.service = new Service();
		}

		if (!this.verify()) {
			return false;
		}

		this.service.setActive(this.serviceActive.isSelected());
		this.service.setApproved(this.serviceApproved.isSelected());
		if (this.service.isApproved()) {
			this.service.setApprovedBy((VinnyUser) this.approver.getSelectedItem());
		}
		this.service.setStartTime(this.calcStartTime(this.firstMonthOfYear));
		this.service.setTimeFrame(((TimeFrame) this.timeFrameCombo.getSelectedItem()).getValue());
		this.service.setDuration(this.calcDuration());
		this.service.setEndTime(this.calcEndTime(this.service.getStartTime(), this.service.getDuration(), this.firstMonthOfYear));

		this.service.setComment(this.commentField.getText());
		final Skill skill = this.controller.getSkill(Skill.GENERAL_SKILL).getResult();
		this.service.setSkill(skill);

		this.service.setVolunteer((Volunteer) this.volunteer.getSelectedItem());

		this.service.setServiceType((ServiceType) this.serviceTypes.getSelectedItem());

		this.saveService();
		return true;
	}

	void durationChanged() {
		try {
			Logger.getAnonymousLogger().info("Duration Field: " + this.durationField.getText());
			final Matcher matcher = this.pattern.matcher(this.durationField.getText());
			if (matcher.find()) {
				final int hour = Integer.parseInt(matcher.group(1));
				final int minute = Integer.parseInt(matcher.group(2));

				final int duration = (hour * 60) + minute;
				if (duration >= 0) {
					this.durationField.setForeground(Color.BLACK);
					final Calendar endTime = this.calcEndTime(this.calcStartTime(this.firstMonthOfYear), duration, this.firstMonthOfYear);
					this.endTimeField.setTime(endTime);
					return;
				}
			}
		} catch (final Exception e) {
			// ignore
		}
		this.durationField.setForeground(Color.RED);
	}

	void endTimeChanged() {
		final Calendar startTime = this.calcStartTime(this.firstMonthOfYear);
		Calendar endTime = this.endTimeField.getTime();

		endTime.set(Calendar.YEAR, startTime.get(Calendar.YEAR));
		endTime.set(Calendar.DAY_OF_YEAR, startTime.get(Calendar.DAY_OF_YEAR));

		long duration = DateUtil.getDifference(startTime, endTime);

		if (duration < 0) {
			endTime = DateUtil.getInstance(startTime);
			this.endTimeField.setTime(endTime);
			this.durationField.setForeground(Color.RED);
			duration = 0;
		}

		this.durationField.setForeground(Color.BLACK);
		this.durationField.setText(DateUtil.formatDuration(duration));
	}

	void startTimeChanged() {
		final Calendar startTime = this.calcStartTime(this.firstMonthOfYear);
		final long duration = this.calcDuration();
		if (duration < 0) {
			this.durationField.setText(DateUtil.formatDuration(0));
			this.endTimeField.setTime(DateUtil.getInstance(startTime));
			this.durationField.setForeground(Color.RED);
		} else {
			final Calendar endTime = this.calcEndTime(startTime, duration, this.firstMonthOfYear);
			this.endTimeField.setTime(endTime);
			this.durationField.setForeground(Color.BLACK);
		}
	}

	void timeFrameChanged() {
		final TimeFrame timeFrame = (TimeFrame) this.timeFrameCombo.getSelectedItem();
		this.endTimeField.setEnabled(timeFrame == TimeFrame.SCAN);
	}

	private long calcDuration() {
		try {
			final Matcher matcher = this.pattern.matcher(this.durationField.getText());
			matcher.find();
			final int hours = Integer.parseInt(matcher.group(1));
			final int minutes = Integer.parseInt(matcher.group(2));
			return (60L * hours) + minutes;
		} catch (final Exception e) {
			// ignore
		}
		return 0;

	}

	private Calendar calcEndTime(final Calendar startTime, final int firstMonth) {
		final TimeFrame timeFrame = (TimeFrame) this.timeFrameCombo.getSelectedItem();

		Calendar endTime = DateUtil.getInstance(startTime);

		if (timeFrame == TimeFrame.SCAN) {
			endTime.set(Calendar.HOUR_OF_DAY, this.endTimeField.getTime().get(Calendar.HOUR_OF_DAY));
			endTime.set(Calendar.MINUTE, this.endTimeField.getTime().get(Calendar.MINUTE));
		} else {
			endTime = DateUtil.getEndOfTimeFrame(startTime, timeFrame, firstMonth);
		}

		return endTime;
	}

	private Calendar calcEndTime(final Calendar startTime, final long duration, final int firstMonth) {
		final TimeFrame timeFrame = (TimeFrame) this.timeFrameCombo.getSelectedItem();

		Calendar endTime = DateUtil.getInstance(startTime);

		if (timeFrame == TimeFrame.SCAN) {
			endTime.add(Calendar.MINUTE, (int) duration);
		} else {
			endTime = DateUtil.getEndOfTimeFrame(startTime, timeFrame, firstMonth);
		}

		return endTime;
	}

	private Calendar calcStartTime(final int firstMonth) {
		final Calendar startTime = this.startDate.getCalendar();
		startTime.set(Calendar.HOUR_OF_DAY, this.startTimeField.getTime().get(Calendar.HOUR_OF_DAY));
		startTime.set(Calendar.MINUTE, this.startTimeField.getTime().get(Calendar.MINUTE));
		final TimeFrame timeFrame = (TimeFrame) this.timeFrameCombo.getSelectedItem();
		final Calendar result = DateUtil.getStartOfTimeFrame(startTime, timeFrame, firstMonth);
		return result;
	}

	private void initComponents() {

		this.serviceActive = new JCheckBox();
		this.serviceActive.setName("Active?");
		this.serviceActive.setSelected(this.service != null ? this.service.isActive() : true);

		this.serviceApproved = new JCheckBox();
		this.serviceApproved.setSelected(this.service != null ? this.service.isApproved() : true);
		this.serviceApproved.setName("Approved?");
		this.serviceApproved.addActionListener(this);

		this.approver = new JComboBox(this.controller.getUsers(VinnyRole.MODIFY_SERVICE).toArray());
		this.approver.setName("Approver");
		if (this.service != null) {
			this.approver.setSelectedItem(this.service.getApprovedBy());
		}

		Calendar displayCalendar = DateUtil.getInstance(this.service != null ? this.service.getStartTime() : DateUtil.getInstance());
		this.startDate = new JCalendarCombo(JCalendarCombo.DISPLAY_DATE, false);
		this.startDate.setName("Start Date");
		this.startDate.setDate(displayCalendar.getTime());
		this.startDate.setDateFormat(DateUtil.DATE_FORMAT);
		this.startDate.addDateListener(new DateListener() {

			@Override
			public void dateChanged(final DateEvent arg0) {
				ServiceEditor.this.startTimeChanged();
			}
		});

		this.startTimeField = new TimeEntry(displayCalendar);
		this.startTimeField.setName("Start Time");
		this.startTimeField.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent arg0) {
				ServiceEditor.this.startTimeChanged();
			}

		});

		displayCalendar = DateUtil.getInstance(this.service != null ? this.service.getEndTime() : DateUtil.getInstance());
		this.endTimeField = new TimeEntry(displayCalendar);
		this.endTimeField.setName("End Time");
		this.endTimeField.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent arg0) {
				ServiceEditor.this.endTimeChanged();
			}
		});

		this.commentField = new JTextField(this.service != null ? this.service.getComment() : "Manually entered hours.");
		this.commentField.setName("Comment");

		this.timeFrameCombo = new JComboBox(TimeFrame.values());
		this.timeFrameCombo.setName("Time Frame");
		this.timeFrameCombo.setSelectedItem(TimeFrame.SCAN);
		if (this.service != null) {
			this.timeFrameCombo.setSelectedItem(this.service.getTimeFrameAsPojo());
		}
		this.timeFrameCombo.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				ServiceEditor.this.timeFrameChanged();
			}
		});

		this.durationField = new JTextField(this.service != null ? DateUtil.formatDuration(this.service.getDuration()) : "hh:mm");
		this.durationField.setName("Duration");
		this.durationField.addKeyListener(new KeyListener() {

			@Override
			public void keyPressed(final KeyEvent e) {
				// ignore
			}

			@Override
			public void keyReleased(final KeyEvent e) {
				ServiceEditor.this.durationChanged();
			}

			@Override
			public void keyTyped(final KeyEvent e) {
				// ignore
			}
		});

		this.volunteer = new JComboBox(this.controller.getVolunteers().toArray());
		this.volunteer.setName("Volunteer");
		if (this.service != null) {
			this.volunteer.setSelectedItem(this.service.getVolunteer());
		}

		this.serviceTypes = new JComboBox(this.controller.getServiceTypes().toArray());
		this.serviceTypes.setName("Service Type");
		if (this.service != null) {
			this.serviceTypes.setSelectedItem(this.service.getServiceType());
		}

		this.add(new LabelPanel(FontUtil.getLabelFont(), Color.black, this.serviceActive, this.volunteer, this.serviceTypes, this.startDate,
				this.startTimeField, this.endTimeField, this.timeFrameCombo, this.durationField, this.serviceApproved, this.approver, this.commentField));
		this.setVisible(true);
	}

	private void saveService() {
		final ApiResult<Service> result = this.controller.saveService(this.service, this.user);
		if (!result.isError()) {
			this.service = result.getResult();
		}
	}

	private boolean verify() {
		final Matcher matcher = this.pattern.matcher(this.durationField.getText());
		return matcher.find();
	}
}
